package com.example.luckybag.service

import android.accessibilityservice.AccessibilityService
import android.graphics.Rect
import android.util.Log
import android.view.accessibility.AccessibilityNodeInfo
import com.example.luckybag.MainActivity

/**
 * 文件名： <br/>
 * 描述：微信抢红包服务类 核心类之一 用于实现自动抢红包
 * 🌝🌝🌝
 *
 * @author wulinran
 * @since 2023/2/10 10:01
 */
class WechatLuckyBagService(service: AccessibilityService) : LuckyBagService(service) {

    private val TAG = "WechatLuckyBagService"

    // "开红包"按钮的id 因为这个会变，所以用个列表，有新的就加进去
    private val weChatOpenBagButtonIdList = arrayListOf<String>("com.tencent.mm:id/giq")
    // 关闭"开红包"页面的弹框按钮的id
    private val luckyCloseButton = "com.tencent.mm:id/gip"


    // "开红包"弹框页面
    private val luckyMoneyNotHookReceiveUI =
        "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI"
    // "开红包"弹框页面 貌似是老页面，因为debug的时候从来也没有获取到过，这个是网上找来的，就放着
    private val luckyMoneyReceiveUI = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI"
    // 红包卡片对应的UI
    private val launcherUI = "com.tencent.mm.ui.LauncherUI"
    // 红包详情页面 识别后马上关闭此页面
    private val luckyMoneyDetailUI = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI"

    override fun dealClassName(className: String, viewGet: ViewGet) {
        when (className) {
            // 加这个 是因为停留在聊天页面时，来红包了或者滑动页面时，也需要触发，暂时没有找到好的方式，暂时用此方案，
            // 为了防止执行太频繁，getLuckyBagCard加了处理
            "androidx.recyclerview.widget.RecyclerView","android.widget.FrameLayout" -> {
                // 这个比较频繁  主要是在聊天页面  有红包来的时候  会触发
                getLuckyBagCard(viewGet.getRootView())
            }

            launcherUI -> {
                // 我管这叫红包卡片页面
                getLuckyBagCard(viewGet.getRootView())
            }

            luckyMoneyReceiveUI -> {
                // 貌似是老UI debug没发现进来
                openLuckyBag(viewGet)
            }

            luckyMoneyNotHookReceiveUI -> {
                // 应该是红包弹框UI新页面 debug进来了
                openLuckyBag(viewGet)
            }

            luckyMoneyDetailUI -> {
                // 红包详情页面  执行关闭操作
                closeLuckyBagDetail(viewGet.getRootView())
            }
        }
    }

    /**
     * 获取所有的红包卡片，用于执行点击，不过这里只点击第一个能点的红包
     *
     * 聊天窗口中的红包都存在"微信红包"一词,因此可根据该词查找红包
     *
     * @param rootInActiveWindow 根部ViewNode
     */
    override fun getLuckyBagCard(rootInActiveWindow: AccessibilityNodeInfo?) {
        if (null != rootInActiveWindow) {
            Log.e(TAG, "获取微信红包")
            val caches: ArrayList<Any> = ArrayList()
            recycleWechat(rootInActiveWindow, caches)
            if (caches.isNotEmpty()) {
                for (index in 0 until caches.size) {
                    if (caches[index] is AccessibilityNodeInfo && (index == 0 || caches[index - 1] !is String)) {
                        val node = caches[index] as AccessibilityNodeInfo
                        var parent = node.parent
                        while (parent != null) {
                            if (parent.isClickable) {
                                parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                                break
                            }
                            parent = parent.parent
                        }
                        break
                    }
                }
            }
        }
    }

    /**
     * 递归查找当前聊天窗口中的红包信息
     *
     * 聊天窗口中的红包都存在"微信红包"一词,因此可根据该词查找红包
     * 这里有个规则，就是匹配到"微信红包"表示是一个红包卡片，记录对应的node，但是有可能是已经结束的红包，所有为了过滤这种，
     * 匹配到"已过期"等字眼时，再记录一个特殊字符，当一个node后面是一个特殊字符时，即表示当前红包不能领取，需要跳过，（这个是根据红包卡片的样式决定的）
     * 当然此处是有bug的，如果有人发了"已过期"等消息的时候，会有问题，会影响上一个红包，当然也只是影响此消息的前一个红包，
     * 这种也是能解决的，只是要加更多的判断，总体考虑这种概率较小，所以不处理了
     *
     * @param node 根node
     * @param caches 用于记录红包缓存的临时数据
     */
    private fun recycleWechat(node: AccessibilityNodeInfo?, caches: ArrayList<Any>) {
        Log.e(TAG+"_recycleWechat", "${node?.viewIdResourceName}  ${node?.className}")
        if (null != node) {
            if (node.childCount == 0) {
                if (node.text != null) {
                    val rect = Rect()
                    node.getBoundsInScreen(rect)

                    if ("已过期" == node.text.toString() || "已被领完" == node.text.toString() || "已领取" == node.text.toString()) {
                        caches.add("#")
                    }

                    if ("微信红包" == node.text.toString()) {
                        caches.add(node)
                    }
                }
            } else {
                for (i in 0 until node.childCount) {
                    if (node.getChild(i) != null) {
                        recycleWechat(node.getChild(i), caches)
                    }
                }
            }
        }
    }

    /**
     *  开红包方法
     *
     * @param viewGet 给内部获取根node的接口 入参用接口而不是用具体的根node，
     * 是因为这里有个sleep,不想影响其它的事件
     *
     * 这里有两种实现方式：
     * 1、通过获取"的红包"的节点，然后通过这个的parent，侧面来获取目标Node,为什么不直接获取，因为目标是个Button,没有文案，
     * 所以无法通过文案直接获取，当然你也可以直接判断Node是不是Button老判断，好像这里只一个Button。这里总体是根据现有的情况
     * 通过侧面来获取木匾Node,一旦微信有变动，也需要跟着改
     *
     * 2、直接通过id获取，这没什么好说的，就是如果微信改版，id变了的话，这个也要新加一个（用了list,因为多个版本，id可能有多个）
     */

    override fun openLuckyBag(viewGet: ViewGet) {
        Log.e(TAG, "点击红包")
        Thread.sleep(100)
        val rootInActiveWindow = viewGet.getRootView()

        if (rootInActiveWindow != null) {
            // 配置开关
            if (MainActivity.GET_WECHAT_LUCKY_BAG_TYPE) {
                // 通过布局内容找对应view
                val list = rootInActiveWindow.findAccessibilityNodeInfosByText("的红包")
                for (i in 0 until list.size) {
                    val parent = list[i].parent
                    if (parent != null) {
                        for (j in 0 until parent.childCount) {
                            val child = parent.getChild(j)
                            if (child != null && child.isClickable) {
                                Log.e(TAG, "点击红包成功")
                                child.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                            }
                        }
                    }
                }
            } else {
                // 通过id找到对应View  目前"开"按钮的id是com.tencent.mm:id/giq  后续发版会变，这种效率高一些  过着使用上述注释的方法，效率低一些，改动的坑更小一些
                var isShowOpenButton = false
                for (index in 0 until weChatOpenBagButtonIdList.size) {
                    val listIds =
                        rootInActiveWindow.findAccessibilityNodeInfosByViewId(
                            weChatOpenBagButtonIdList[index]
                        )
                    if (listIds.isNotEmpty()) {
                        listIds[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
                        isShowOpenButton = true
                        break
                    }
                }

                // 因为有时候红包被抢完了，或者过期了，需要关闭弹框，不然会卡住，影响下次抢红包
                if(!isShowOpenButton){
                    val closes = rootInActiveWindow.findAccessibilityNodeInfosByViewId(luckyCloseButton)
                    if (closes.isNotEmpty()) {
                        closes[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
                    }
                }

            }
        }
    }

    /**
     * 关闭红包详情页
     *
     * 简单说，就是对于抢红包这件事来说，这个页面就是多余的，只对于"抢"这件事哈，原理跟上面方法1是一样的
     *
     * @param rootInActiveWindow 根node
     */

    override fun closeLuckyBagDetail(rootInActiveWindow: AccessibilityNodeInfo?) {
        if(null != rootInActiveWindow) {
            val list = rootInActiveWindow.findAccessibilityNodeInfosByText("的红包")
            if (list.isNotEmpty()) {
                val parent = list[0].parent.parent.parent
                if (parent != null) {
                    for (j in 0 until parent.childCount) {
                        val child = parent.getChild(j)
                        if (child != null && child.isClickable) {
                            child.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                        }
                    }

                }

            }
        }
    }
}